package com.di3fang.gateway.config.security.token;

import com.di3fang.gateway.config.security.vo.SecurityProperties;
import io.jsonwebtoken.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@Component
public class TokenProvider {

    @Autowired
    private SecurityProperties properties;

    /**
     * 创建token
     *
     * @param authentication 认证类,支持自定义
     * @return
     */
    public String createToken(Authentication authentication) {

        String authorities = authentication.getAuthorities().stream()
                .map(GrantedAuthority::getAuthority)
                .collect(Collectors.joining(","));

        Date validity = new Date(System.currentTimeMillis() + properties.getTokenValidityInSeconds());
        String token = Jwts.builder()
                .setSubject(authentication.getName())
                .claim(properties.getAuthoritiesKey(), authorities)
                .signWith(SignatureAlgorithm.HS512, properties.getBase64Secret())
                .setExpiration(validity)
                .compact();
        return token;
    }

    public Authentication getAuthentication(String token) {
        Claims claims = Jwts.parser()
                .setSigningKey(properties.getBase64Secret())
                .parseClaimsJws(token)
                .getBody();

        Collection<? extends GrantedAuthority> authorities =
                Arrays.stream(claims.get(properties.getAuthoritiesKey()).toString().split(","))
                        .map(SimpleGrantedAuthority::new)
                        .collect(Collectors.toList());

        User principal = new User(claims.getSubject(), "", authorities);

        return new UsernamePasswordAuthenticationToken(principal, token, authorities);
    }

    public boolean validateToken(String authToken) {
        try {
            Jwts.parser().setSigningKey(properties.getBase64Secret()).parseClaimsJws(authToken);
            return true;
        } catch (SecurityException | MalformedJwtException e) {
            log.error("Invalid JWT signature.");
        } catch (ExpiredJwtException e) {
            log.error("Expired JWT token.");
        } catch (UnsupportedJwtException e) {
            log.error("Unsupported JWT token.");
        } catch (IllegalArgumentException e) {
            log.error("JWT token compact of handler are invalid.");
        } catch (Exception e) {
            log.error("JWT signature does not match");
        }
        return false;
    }

    /**
     * 判断令牌是否过期
     *
     * @param token 令牌
     * @return 是否过期
     */
    public Boolean isTokenExpired(String token) {
        try {
            Claims claims = Jwts.parser()
                    .setSigningKey(properties.getBase64Secret())
                    .parseClaimsJws(token)
                    .getBody();
            Date expiration = claims.getExpiration();
            return expiration.before(new Date());
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 获取角色信息
     */
    public List<String> getAuthorities() {
        String token = getToken();
        Claims claims = Jwts.parser()
                .setSigningKey(properties.getBase64Secret())
                .parseClaimsJws(token)
                .getBody();
        return Arrays.stream(claims.get(properties.getAuthoritiesKey()).toString().split(","))
                .map(String::new)
                .collect(Collectors.toList());
    }


    public String getToken() {
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = servletRequestAttributes.getRequest();
        final String requestHeader = request.getHeader(properties.getHeader());
        if (requestHeader != null) {
            return requestHeader;
        }
        return null;
    }

    /**
     * 从token中获取用户信息
     */
    public String getUserFromToken() {
        String token = getToken();
        return Jwts.parser()
                .setSigningKey(properties.getBase64Secret())
                .parseClaimsJws(token)
                .getBody().getSubject();
    }

}
